package com.bingxue.edu.management.shop.service;

import com.bingxue.edu.common.enums.OrderStatusEnum;
import com.bingxue.edu.framework.mybatis.base.BaseService;
import com.bingxue.edu.management.report.model.query.FinAccountStatQuery;
import com.bingxue.edu.management.report.model.resp.FinAccountStatResp;
import com.bingxue.edu.management.shop.mapper.FinAccountMapper;
import com.bingxue.edu.management.shop.model.entity.FinAccount;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

import static com.bingxue.edu.management.shop.model.entity.table.FinAccountTableDef.FIN_ACCOUNT;
import static com.bingxue.edu.management.shop.model.entity.table.OrderTableDef.ORDER;
import static com.bingxue.edu.management.shop.model.entity.table.RefundTableDef.REFUND;
import static com.mybatisflex.core.query.QueryMethods.ifNull;
import static com.mybatisflex.core.query.QueryMethods.number;
import static com.mybatisflex.core.query.QueryMethods.sum;

/**
 * 财务账户业务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FinAccountService extends BaseService<FinAccountMapper, FinAccount> {

    private final OrderService orderService;
    private final RefundService refundService;

    @Transactional(rollbackFor = Exception.class)
    public void remove(List<String> ids) {
        this.removeByIds(ids);
    }

    /**
     * 返回账户收支统计列表
     * order表的pay_time用来时间筛选
     * refund表的create_time用来时间筛选
     */
    public List<FinAccountStatResp> getFinAccountStat(FinAccountStatQuery query) {
        QueryWrapper orderQueryWrapper = QueryWrapper.create()
                .select(FIN_ACCOUNT.DEFAULT_COLUMNS,
                        QueryMethods.count(ORDER.ID).as(FinAccountStatResp::getOrderCount),
                        ifNull(sum(ORDER.ACTUAL_TOTAL_PRICE), number(0)).as(FinAccountStatResp::getOrderPrice)
                )
                .from(ORDER)
                .leftJoin(FIN_ACCOUNT).on(ORDER.FIN_ACCOUNT_ID.eq(FIN_ACCOUNT.ID))
                .where(ORDER.STATUS.eq(OrderStatusEnum.PAID))
                .and(ORDER.PAY_TIME.ge(query.getDateFrom()))
                .and(ORDER.PAY_TIME.lt(query.getDateTo()))
                .groupBy(ORDER.FIN_ACCOUNT_ID);

        QueryWrapper refundQueryWrapper = QueryWrapper.create()
                .select(FIN_ACCOUNT.DEFAULT_COLUMNS,
                        QueryMethods.count(REFUND.ID).as(FinAccountStatResp::getRefundCount),
                        ifNull(sum(REFUND.REFUND_PRICE), number(0)).as(FinAccountStatResp::getRefundPrice)
                )
                .from(REFUND)
                .leftJoin(FIN_ACCOUNT).on(REFUND.FIN_ACCOUNT_ID.eq(FIN_ACCOUNT.ID))
                .where(REFUND.REFUND_OFF.eq(true))
                .and(REFUND.REFUND_TIME.ge(query.getDateFrom()))
                .and(REFUND.REFUND_TIME.lt(query.getDateTo()))
                .groupBy(REFUND.FIN_ACCOUNT_ID);

        List<FinAccountStatResp> orderResult = orderService.listAs(orderQueryWrapper, FinAccountStatResp.class);
        List<FinAccountStatResp> refundResult = refundService.listAs(refundQueryWrapper, FinAccountStatResp.class);

        List<FinAccountStatResp> mergedResult = new ArrayList<>();
        for (FinAccountStatResp resp : orderResult) {
            FinAccountStatResp filtered = mergedResult.stream()
                    .filter(item -> item.getId().equals(resp.getId()))
                    .findFirst().orElse(null);
            if (filtered == null) {
                mergedResult.add(resp);
            } else {
                filtered.setOrderCount(resp.getOrderCount());
                filtered.setOrderPrice(resp.getOrderPrice());
            }
        }
        for (FinAccountStatResp resp : refundResult) {
            FinAccountStatResp filtered = mergedResult.stream()
                    .filter(item -> item.getId().equals(resp.getId()))
                    .findFirst().orElse(null);
            if (filtered == null) {
                mergedResult.add(resp);
            } else {
                filtered.setRefundCount(resp.getRefundCount());
                filtered.setRefundPrice(resp.getRefundPrice());
            }
        }

        return mergedResult;
    }
}
